use std::ptr;

use serde::{Deserialize, Serialize};
use tokio::io::AsyncWriteExt;

use crate::{
    error::{new_err, AppErr},
    utils::{encode_u16, encode_u8, new_buf},
};

use super::reader::{HEAD, MIN_FRAME_LEN};

pub const PING: u8 = 0;
pub const PONG: u8 = 1;
pub const ACK: u8 = 2;
pub const REQ: u8 = 3;
pub const RES: u8 = 4;
pub const NOTIFY: u8 = 5;
pub const NOTIFY_ACK: u8 = 6;

#[derive(Debug)]
pub struct Frame {
    pub c_type: u8,
    pub seq: u8,
    pub body: Option<Box<[u8]>>,
}

impl Frame {

    pub fn id(&self) -> u16 {
        ((self.c_type as u16) << 8) + (self.seq as u16)
    }

    pub fn cmd(&self) -> Result<u8, AppErr> {
        let body = self.body.as_ref().ok_or(AppErr::TcpProto("body is none"))?;
        if body.len() < 1 {
            return Err(AppErr::TcpProto("body len < 1? "));
        }
        Ok(body[0])
    }

    pub fn parse<'a, T: Deserialize<'a>>(&'a self) -> Result<T, AppErr> {
        let body: &[u8] = self.body.as_ref().ok_or(AppErr::TcpProto("body is none"))?;
        if body.len() < 1 {
            return Err(AppErr::TcpProto("body len < 1? "));
        }
        Ok(serde_cbor::from_slice(&body[1..]).map_err(|_| AppErr::TcpProto("parse fail"))?)
    }

    pub fn body(self) -> Result<Box<[u8]>, AppErr> {
        let body = self.body.ok_or(AppErr::TcpProto("body is none"))?;
        Ok( body )
    }
}

pub fn make_simple_frame(c_type: u8, seq: u8) -> Box<[u8]> {
    let mut buf = new_buf(MIN_FRAME_LEN);
    encode_u16(&mut buf, HEAD);
    encode_u16(&mut buf[2..], MIN_FRAME_LEN as u16);
    encode_u8(&mut buf[4..], c_type);
    encode_u8(&mut buf[5..], seq);
    buf
}

pub fn new_id(c_type: u8, seq: u8) -> u16 {
    ((c_type as u16) << 8) + (seq as u16)
}

pub fn make_frame(c_type: u8, seq: u8, cmd: u8, data: &[u8]) -> Box<[u8]> {
    let size = MIN_FRAME_LEN + 1 + data.len();
    let mut buf = new_buf(size);
    encode_u16(&mut buf, HEAD);
    encode_u16(&mut buf[2..], size as u16);
    encode_u8(&mut buf[4..], c_type);
    encode_u8(&mut buf[5..], seq);

    encode_u8(&mut buf[6..], cmd);
    unsafe {
        let dst = &mut buf[7..];
        ptr::copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len());
    }

    buf
}

pub async fn write<W: AsyncWriteExt + Unpin, T: Serialize>(
    w: &mut W,
    c_type: u8,
    seq: u8,
    cmd: u8,
    body: &T,
) -> Result<(), AppErr> {
    let vec = serde_cbor::to_vec(body).map_err(|_| new_err("serial fail"))?;
    let buf = make_frame(c_type, seq, cmd, &vec);
    w.write_all(&buf).await?;
    Ok(())
}
